package com.gbei.center.oauth.controller;

import com.gbei.center.common.Constants;
import com.gbei.center.enums.ExpireEnum;
import com.gbei.center.enums.GrantTypeEnum;
import com.gbei.center.oauth.model.*;
import com.gbei.center.oauth.service.AuthorizationService;
import com.gbei.center.oauth.service.LoginService;
import com.gbei.center.oauth.service.RedisService;
import com.gbei.center.oauth.service.UserService;
import com.gbei.center.utils.DateUtils;
import com.gbei.center.utils.message.ResponseInfo;
import com.gbei.center.utils.message.Result;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 基于oauth2.0相关的授权相关操作
 *
 */
@Controller
@RequestMapping("/oauth2.0")
public class OauthController {

    @Resource(name = "redisServiceImpl")
    private RedisService redisService;

    @Resource(name = "authorizationServiceImpl")
    private AuthorizationService authorizationService;

    @Resource(name = "userServiceImpl")
    private UserService userService;

    @Resource(name = "loginServiceImpl")
    private LoginService loginService;

    /**
     * 注册需要接入的客户端信息
     * @return java.util.Map<java.lang.String,java.lang.Object>
     */
    @PostMapping(value = "/clientRegister", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    @ResponseBody
    public Map<String,Object> clientRegister(@RequestBody(required = true) AuthClientDetails clientDetails){
        Map<String,Object> result = new HashMap<>(2);

        boolean registerRet = authorizationService.register(clientDetails);
        if(registerRet){
            result.put("code","200");
        }else{
            result.put("code","500");
            result.put("msg","注册失败");
        }

        return result;
    }

    /**
     * 授权页面
     * @return org.springframework.web.servlet.ModelAndView
     */
    @RequestMapping("/authorizePage")
    public ModelAndView authorizePage(HttpServletRequest request){
        ModelAndView modelAndView = new ModelAndView("authorize");

        //在页面同意授权后的回调地址
        String redirectUrl = request.getParameter("redirectUri");
        //客户端ID
        String clientId = request.getParameter("client_id");
        //权限范围
        String scope = request.getParameter("scope");

        if(StringUtils.isNoneBlank(redirectUrl)){
            HttpSession session = request.getSession();
            //将回调地址添加到session中
            session.setAttribute(Constants.SESSION_AUTH_REDIRECT_URL,redirectUrl);
        }

        //查询请求授权的客户端信息
        AuthClientDetails clientDetails = authorizationService.selectClientDetailsByClientId(clientId);
        modelAndView.addObject("clientId", clientId);
        modelAndView.addObject("clientName", clientDetails.getClientName());
        modelAndView.addObject("scope", scope);

        return modelAndView;
    }

    /**
     * 同意授权
     * @param request HttpServletRequest
     * @return java.util.Map<java.lang.String,java.lang.Object>
     */
    @PostMapping(value = "/agree", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    @ResponseBody
    public Map<String,Object> agree(HttpServletRequest request){
        Map<String,Object> result = new HashMap<>(2);
        HttpSession session = request.getSession();

        //客户端ID
        String clientIdStr = request.getParameter("client_id");
        //权限范围
        String scopeStr = request.getParameter("scope");

        if(StringUtils.isNoneBlank(clientIdStr) && StringUtils.isNoneBlank(scopeStr)){
            PlatformUser user = (PlatformUser) session.getAttribute(Constants.SESSION_USER);

            //1. 向数据库中保存授权信息
            boolean saveFlag = authorizationService.saveAuthClientUser(user.getUserId(), clientIdStr, scopeStr);

            //2. 返回给页面的数据
            if(saveFlag){
                result.put("code",200);
                //授权成功之后的回调地址
                String redirectUrl = (String) session.getAttribute(Constants.SESSION_AUTH_REDIRECT_URL);
                session.removeAttribute(Constants.SESSION_AUTH_REDIRECT_URL);
                if(StringUtils.isNoneBlank(redirectUrl)){
                    result.put("redirect_uri", redirectUrl);
                }
            }else{
                result.put("msg", "授权失败！");
            }
        }else{
            result.put("msg", "请求参数不能为空！");
        }
        return result;
    }

    /**
     * 获取Authorization Code
     * @param request HttpServletRequest
     * @return org.springframework.web.servlet.ModelAndView
     */
    @RequestMapping("/authorize")
    public ModelAndView authorize(HttpServletRequest request){
        HttpSession session = request.getSession();
        PlatformUser user = (PlatformUser) session.getAttribute(Constants.SESSION_USER);

        //客户端ID
        String clientIdStr = request.getParameter("client_id");
        //权限范围
        String scopeStr = request.getParameter("scope");
        //回调URL
        String redirectUri = request.getParameter("redirect_uri");
        //state，用于防止CSRF攻击（非必填）
        String state = request.getParameter("state");

        //生成Authorization Code
        String authorizationCode = authorizationService.createAuthorizationCode(clientIdStr, scopeStr, user);

        String params = "?code=" + authorizationCode;
        if(StringUtils.isNoneBlank(state)){
            params = params + "&state=" + state;
        }

        return new ModelAndView("redirect:" + redirectUri + params);
    }

    /**
     * 通过Authorization Code获取Access Token
     * @param request HttpServletRequest
     * @return java.util.Map<java.lang.String,java.lang.Object>
     */
    @RequestMapping(value = "/token", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    @ResponseBody
    public Result<?> token(HttpServletRequest request){
        Map<String,Object> result = new HashMap<>(8);

        //授权方式
        String grantType = request.getParameter("grant_type");
        //前面获取的Authorization Code
        String code = request.getParameter("code");
        //客户端ID
        String clientIdStr = request.getParameter("client_id");
        //接入的客户端的密钥
        String clientSecret = request.getParameter("client_secret");

        //校验授权方式
        if(!GrantTypeEnum.AUTHORIZATION_CODE.getType().equals(grantType) && !GrantTypeEnum.CLIENT_CREDENTIALS.getType().equals(grantType)){
            return ResponseInfo.UNSUPPORTED_GRANT_TYPE.messageToResult();
        }

        try {
            AuthClientDetails savedClientDetails = authorizationService.selectClientDetailsByClientId(clientIdStr);
            //校验请求的客户端秘钥和已保存的秘钥是否匹配
            if(!(savedClientDetails != null && savedClientDetails.getClientSecret().equals(clientSecret))){
                return ResponseInfo.INVALID_CLIENT.messageToResult();
            }

            //根据clientId获取scope
            StringBuffer scopeStr = new StringBuffer();
            List<AuthClientScope> list = authorizationService.listAuthClientScope(savedClientDetails.getId());
            for (int i = 0; i < list.size(); i++) {
                AuthScope authScope = authorizationService.getAuthScope(list.get(i).getAuthScopeId());
                if (i == list.size() - 1){
                    scopeStr.append(authScope.getScopeName());
                }else {
                    scopeStr.append(authScope.getScopeName() + ",");
                }
            }
            String scope = scopeStr.toString();

            //校验授权方式  客户端接入
            if(GrantTypeEnum.CLIENT_CREDENTIALS.getType().equals(grantType) ){
                //过期时间
                Long expiresIn = DateUtils.dayToSecond(ExpireEnum.ACCESS_TOKEN.getTime());

                //生成Access Token
                String accessTokenStr = authorizationService.createAccessToken(savedClientDetails, grantType, scope, expiresIn);
                //查询已经插入到数据库的Access Token
                AuthAccessToken authAccessToken = authorizationService.selectByAccessToken(accessTokenStr);
                //生成Refresh Token
                String refreshTokenStr = authorizationService.createRefreshToken(authAccessToken);

                //返回数据
                result.put("access_token", authAccessToken.getAccessToken());
                result.put("refresh_token", refreshTokenStr);
                result.put("expires_in", expiresIn);
                result.put("scope", scope);
                return ResponseInfo.SUCCESS.messageToResult(result);
            }

            //从Redis获取对应的用户信息
            PlatformUser user = redisService.get(code + ":user");

            //如果能够通过Authorization Code获取到对应的用户信息，则说明该Authorization Code有效
            if(StringUtils.isNoneBlank(scope) && user != null){
                //过期时间
                Long expiresIn = DateUtils.dayToSecond(ExpireEnum.ACCESS_TOKEN.getTime());

                //生成Access Token
                String accessTokenStr = authorizationService.createAccessToken(user, savedClientDetails, grantType, scope, expiresIn);
                //查询已经插入到数据库的Access Token
                AuthAccessToken authAccessToken = authorizationService.selectByAccessToken(accessTokenStr);
                //生成Refresh Token
                String refreshTokenStr = authorizationService.createRefreshToken(user, authAccessToken);

                //返回数据
                result.put("access_token", authAccessToken.getAccessToken());
                result.put("refresh_token", refreshTokenStr);
                result.put("expires_in", expiresIn);
                result.put("scope", scope);
                return ResponseInfo.SUCCESS.messageToResult(result);
            }else{
                return ResponseInfo.INVALID_GRANT.messageToResult();
            }
        }catch (Exception e){
            e.printStackTrace();
            return ResponseInfo.FAILURE.messageToResult();
        }
    }

    /**
     * 通过Refresh Token刷新Access Token
     * @param request HttpServletRequest
     * @return java.util.Map<java.lang.String,java.lang.Object>
     */
    @RequestMapping(value = "/refreshToken", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    @ResponseBody
    public Result<?> refreshToken(HttpServletRequest request){
        Map<String,Object> result = new HashMap<>(8);

        //客户端ID
        String clientIdStr = request.getParameter("client_id");

        //接入的客户端的密钥
        String clientSecret = request.getParameter("client_secret");

        AuthClientDetails savedClientDetails = authorizationService.selectClientDetailsByClientId(clientIdStr);

        //校验请求的客户端秘钥和已保存的秘钥是否匹配
        if(!(savedClientDetails != null && savedClientDetails.getClientSecret().equals(clientSecret))){
            return ResponseInfo.INVALID_CLIENT.messageToResult();
        }

        //获取Refresh Token
        String refreshTokenStr = request.getParameter("refresh_token");

        try {
            AuthRefreshToken authRefreshToken = authorizationService.selectByRefreshToken(refreshTokenStr);

            if(authRefreshToken != null) {
                Long savedExpiresAt = authRefreshToken.getExpiresIn();
                //过期日期
                LocalDateTime expiresDateTime = DateUtils.ofEpochSecond(savedExpiresAt, null);
                //当前日期
                LocalDateTime nowDateTime = DateUtils.now();

                //如果Refresh Token已经失效，则需要重新生成
                if (expiresDateTime.isBefore(nowDateTime)) {
                    return ResponseInfo.EXPIRED_TOKEN.messageToResult();
                } else {
                    //获取存储的Access Token
                    AuthAccessToken authAccessToken = authorizationService.selectByAccessId(authRefreshToken.getTokenId());

                    //新的过期时间
                    Long expiresIn = DateUtils.dayToSecond(ExpireEnum.ACCESS_TOKEN.getTime());

                    String newAccessTokenStr;

                    if ("client_credentials".equals(authAccessToken.getGrantType())){
                        newAccessTokenStr = authorizationService.createAccessToken(savedClientDetails
                                , authAccessToken.getGrantType(), authAccessToken.getScope(), expiresIn);
                    }else {
                        //获取对应的用户信息
                        PlatformUser user = loginService.selectByUserId(authAccessToken.getUserId());

                        //生成新的Access Token
                         newAccessTokenStr = authorizationService.createAccessToken(user, savedClientDetails
                                , authAccessToken.getGrantType(), authAccessToken.getScope(), expiresIn);
                    }

                    //返回数据
                    result.put("access_token", newAccessTokenStr);
                    result.put("refresh_token", refreshTokenStr);
                    result.put("expires_in", expiresIn);
                    result.put("scope", authAccessToken.getScope());
                    return ResponseInfo.SUCCESS.messageToResult(result);
                }
            }else {
                return ResponseInfo.INVALID_GRANT.messageToResult();
            }
        }catch (Exception e){
            e.printStackTrace();
            return ResponseInfo.FAILURE.messageToResult();
        }
    }

}
